require( "litesocket" )
E2Lib.RegisterExtension("ESC", true)

------------------------------------------------------------
----------------Expression2 Socket Core---------------------
------------------made by: Fun Maker------------------------
------------------------------------------------------------

local SOCKETs = {}
local PACKETs = {}
local s={}

local OnlyAdmin=CreateConVar( "esc_onlyAdmin", "0",FCVAR_ARCHIVE )
local MaxSize=CreateConVar( "esc_maxPacketSize", "-1",FCVAR_ARCHIVE )
local MaxSocketsPerPlayer=CreateConVar( "esc_maxSocketsPerPlayer", "5",FCVAR_ARCHIVE )

local SteamIDwhitelist = {}

SteamIDwhitelist["STEAM_0:0:19431127"] = true
SteamIDwhitelist["STEAM_0:other:steamid"] = true  ---- ADD your IDs here


 
concommand.Add( "esc_getPorts", function( ply, command, arguments )
	if not ply:IsAdmin() then return end
	for k, v in pairs(player.GetAll( )) do
		for k2, v2 in pairs(v.sockets) do
			ply:PrintMessage( HUD_PRINTCONSOLE, "port: "..k2.." by: "..v:GetName() )
		end
	end
end )

concommand.Add( "esc_closeSocket", function( ply, command, arguments )
	if not ply:IsAdmin() then return end
	local port = tonumber(arguments[1])
	if SOCKETs[port]==nil then return end
	SOCKETs[port]:Close()
	SOCKETs[port]=nil
	for k, v in pairs(player.GetAll( )) do
		v.sockets[port]=nil
	end
end )

concommand.Add( "esc_getSockets", function( ply, command, arguments )
	if not ply:IsAdmin() then return end
	for k, v in pairs(SOCKETs) do
		ply:PrintMessage( HUD_PRINTCONSOLE, "port: "..k)
	end
end )


concommand.Add( "esc_getWhiteList", function( ply, command, arguments )
	for k, v in pairs(SteamIDwhitelist) do
		ply:PrintMessage( HUD_PRINTCONSOLE, "ID: "..k)
	end
end )

concommand.Add( "esc_whiteListAdd", function( ply, command, arguments )
	if not ply:IsAdmin() then return end 
	SteamIDwhitelist[arguments[1]]=true 
end )

concommand.Add( "esc_whiteListRemove", function( ply, command, arguments )
	if not ply:IsAdmin() then return end
	SteamIDwhitelist[arguments[1]]=nil
end )



local run_on = {
	clk = 0,
	ents = {}
}

e2function void runOnPacket( number roPacket )
	run_on.ents[self.entity]=self.entity
end


registerType("socket", "xsc", 0,
	nil,
	nil,
	function(retval)
		if type(retval) ~= "number" then error("Return value is not a number, but a "..type(retval).."!",0) end
	end,
	function(v)
		return type(v) ~= "number"
	end
)

local DEF = {{},"",0,0}
registerType("packet", "xpc", table.Copy(DEF),
	nil,
	nil,
	function(retval)
		if type(retval) ~= "table" then error("Return value is neither a Packet nor a table, but a "..type(retval).."!",0) end
		if #retval ~= 4 then error("Return value does not have exactly 4 entries!",0) end
	end,
	function(v)
		return not ( type(v) == "table" and v[1] ~= nil and v[1][1] ~= nil)
	end
)

e2function number operator_is(socket c)
	if not c or c < 0 or SOCKETs[c] == nil then
		return 0 
	else
		return 1
	end
end

local function canUseSocket( player, size ) 
	if player:IsAdmin() then 
		return true 
	end
	if OnlyAdmin:GetInt() != nil and OnlyAdmin:GetInt() > 0 and SteamIDwhitelist[player:SteamID()] == false then 
		return false 
	end
    if MaxSize:GetInt() != nil and MaxSize:GetInt() > 0 and size != nil and size > MaxSize:GetInt() then 
		return false 
	end
	--if MaxSocketsPerPlayer:GetInt() != nil and MaxSocketsPerPlayer:GetInt() > 0 and #player.sockets >= MaxSocketsPerPlayer:GetInt() then
	
	return true
end

e2function void socket:send(packet packet, vector4 IP, number port)
   if canUseSocket( self.player, #packet[1] ) then
		
		if SOCKETs[this] == nil then 
			error("Socket: "..this.." is close!") 
			return -1
		end
		
		SOCKETs[this]:SendTo(SocketConcatTable(packet[1],#packet[1]),#packet[1]+1,IP[1].."."..IP[2].."."..IP[3].."."..IP[4],port)
		print("Send")
	end
end

e2function number socket:isValid()
	if this == nil then return -22 end
	return this
end

e2function socket socket(number port)
	local ply = self.player
	if ply.sockets == nil then
		ply.sockets = {}
	end
	if SOCKETs[port] ~= nil then
		error("Port: "..port.." in use.")
		return -1
	end
	if !canUseSocket( ply ) then
		error("You don't have permission to use Sockets")
		return -1
	end
	if not ply:IsAdmin() and MaxSocketsPerPlayer:GetInt() != nil and MaxSocketsPerPlayer:GetInt() > 0 and #ply.sockets >= MaxSocketsPerPlayer:GetInt() then
		error("You cannot make more socket than: "..MaxSocketsPerPlayer:GetInt())
		return -1
	end
	if SOCKETs[port] == nil then
		s = Socket(IPPROTO_UDP)
		s:Bind("*",port)
		SOCKETs[port]=s
		ply.sockets[port]=true
	end
	print("Create Socket: "..port)
	return port
end

e2function number socketGetPort(socket sck)
	return sck or -1
end

e2function void socketClose(socket sock)
  if sock and SOCKETs[sock] then
		SOCKETs[sock]:Close()
		SOCKETs[sock]=nil
		for k, v in pairs(player.GetAll( )) do
			v.sockets[sock]=nil
		end
	end
end

e2function void socketCloseAll()
  local ply = self.player
	ply.sockets = ply.sockets or {}
	for k, v in pairs(ply.sockets) do
		if SOCKETs[k] then
			SOCKETs[k]:Close()
			SOCKETs[k] = nil
			ply.sockets[k] = nil
		end
	end
end

e2function socket operator=(socket lhs, socket rhs)
	self.vars[lhs] = rhs
	self.vclk[lhs] = true
	return rhs
end




hook.Add("Think","SocketRecv",function()
	for k,v in pairs(SOCKETs) do
		while true do
			local d, ip, port, ln = v:RecvFromTable()
			local ROP = 0
			if d then
				if PACKETs[k] == nil then PACKETs[k] = {} end
				table.insert(PACKETs[k],{d, ip, port, ln})
				print("packet from: "..ip..":"..port..", "..ln..", "..table.concat(d,",")..SocketConcatTable(d,ln))
				ROP = 1
			else 
				break
			end
			if ROP == 1 then
				
				run_on.clk = 1
				
				for k,v in pairs( run_on.ents ) do
					if validEntity( v ) and v.Execute then
						if v.player.sockets and v.player.sockets[port] then
							v:Execute()
						end
					end
				end
				
				run_on.clk = 0
			end
		end
	end
end) 

e2function number packetClk()
	return run_on.clk
end

e2function packet socket:recv()
  if PACKETs[this] ~= nil and PACKETs[this] ~= {} then
		return table.remove(PACKETs[this],1)
	end
	return {{},"",0,0}
end

e2function number packet:dataCount()
	if this == nil or this[1] == nil then return 0 end
	return #this[1]
end




e2function packet packet:push1B(number data)
	if this == nil or this[1] == nil then return end
	if data<0 then data = data + 128 end
	
	table.insert(this[1],math.floor(math.Clamp(data,0,255)))
	--print("added: "..math.floor(math.Clamp(data,0,255)).." to "..table.concat(this[1],","))
	return this
end

e2function packet packet:push2B(number data)
	if this == nil or this[1] == nil then return end
	if data<0 then data = data + 32768 end
	
	this[1]=table.Add(this[1],{math.floor(math.Clamp(data/256,0,255)),math.floor(data)%256})
	return this
end


e2function packet packet:pushString(string data)
	if this == nil or this[1] == nil then return end
	this[1]=table.Add(this[1],{string.byte(data,1,string.len(data))})
	table.insert(this[1],0)
	--print("added: "..data.." as "..table.concat({string.byte(data,1,string.len(data))},","))
	return this
end

e2function packet packet:pushStringNoNull(string data)
	if this == nil or this[1] == nil then return end
	this[1]=table.Add(this[1],{string.byte(data,1,string.len(data))})
	--print("added: "..data.." as "..table.concat({string.byte(data,1,string.len(data))},","))
	return this
end



e2function number packet:get1B()
	if this == nil or this[1] == nil or this[1][1] == nil then return 0 end
	return table.remove(this[1],1)
end

e2function number packet:get2B()
	if this == nil or this[1] == nil or this[1][1] == nil then return 0 end
	return table.remove(this[1],1)*256+table.remove(this[1],1)
end

e2function number packet:get1BSign()
	if this == nil or this[1] == nil or this[1][1] == nil then return 0 end
	return table.remove(this[1],1)-128
end

e2function number packet:get2BSign()
	if this == nil or this[1] == nil or this[1][1] == nil then return 0 end
	return table.remove(this[1],1)*256+table.remove(this[1],1)-32768
end


e2function string packet:getString()
	if this == nil or this[1] == nil or this[1][1] == nil then return "" end
	local S = ""
	
	while this[1][1] ~= nil do
		n = table.remove(this[1],1)
		if n==nil or n == 0 then break end
		S = S..string.char(n)
	end
	return S
end

e2function string packet:getStringNoNull(number len)
	if this == nil or this[1] == nil or this[1][1] == nil then return "" end
	local S = ""
	local I = len
	
	while this[1][1] ~= nil and I do
		I = I - 1
		n = table.remove(this[1],1)
		if n == nil or n == 0 then break end
		S = S..string.char(n)
	end
	return S
end

e2function string packet:getSuperString()
	if this == nil or this[1] == nil or not #this[1]  then return "" end
	local S = ""
	while #this[1] do
		n = table.remove(this[1],1)
		if n==nil then break end
		S = S..string.char(n)
	end
	return S
end

e2function vector4 packet:getIP()
	IP=string.Explode(".",this[2])
	if #IP ~= 4 then return {0,0,0,0} end
	IP={tonumber(IP[1]),tonumber(IP[2]),tonumber(IP[3]),tonumber(IP[4])}
	return IP
end

e2function number packet:getPort()

	if this==nil or this[3]==nil then return 0 end
	return this[3]
end


e2function packet packet()
	return {{},"",0,0}
end


e2function packet operator=(packet lhs, packet rhs)
	self.vars[lhs] = rhs
	self.vclk[lhs] = true
	return rhs
end

e2function number operator_is(packet c)
	if c == nil or c[1] == nil or not #c[1] then
		return 0 
	else
		return 1
	end
end